/* Algoritmo de monte carlo para aproximação do PI utilizando pthreads */

#include <gmp.h>
#include <pthread.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#define NUMTHREADS 2
#define myrand ((float)(random())/(float)(RAND_MAX))

//estrutura de passagem de parâmetro para as threads
//memória distribuída
typedef struct data {
	int t_id;
	double x, y; //posição do ponto
	mpf_t pdentro; //quantidade de pontos contidos dentro do quarto de circunferencia
	mpf_t ptotal; //quantidade total de pontos
} t_data;

//inicializa os dados iniciais das memórias distribuídas das threads
void init_tdata(t_data *data, int id) {

	int z = 0;
	mpf_init(data->pdentro);
	mpf_init(data->ptotal);
	mpf_set_prec(data->pdentro, 33300000);
	mpf_set_prec(data->ptotal, 33300000);
	mpf_set_si(data->pdentro,z);
	mpf_set_si(data->ptotal,z);
	data->x = 0;
	data->y = 0;
	data->t_id = id;

}

//executa o algoritmo Monte Carlo
void *monte_carlo(void *targ) {

	int i = 0;
	t_data *inner_data = (t_data*) targ;
	
	srand ( time(NULL) + inner_data->t_id );

	for ( i = 0; i < 100000000/NUMTHREADS; i++ ) {
	
		inner_data->x = myrand;
		inner_data->y = myrand;
	
		//verifica se a posição do ponto está contida no quarto de circunferencia
		double sqr = pow(inner_data->x,2) + pow(inner_data->y,2);

		if ( sqr <= 1 ) {
			//adiciona 1 aos pontos contidos no espaço desejado
			mpf_add_ui(inner_data->pdentro, inner_data->pdentro, 1);
			mpf_add_ui(inner_data->ptotal, inner_data->ptotal, 1);
		} else {
			//adiciona 1 à quantidade total de pontos
			mpf_add_ui(inner_data->ptotal, inner_data->ptotal, 1);
		}

	}

	pthread_exit(NULL);

}

int main() {

	int i = 0;
	int rc;
	mpf_t pi, ptot, pin;
	FILE *f;

	mpf_init(pi);
	mpf_init(pin);
	mpf_init(ptot);
	mpf_set_prec(pi,33300000);
	mpf_set_prec(pin,33300000);
	mpf_set_prec(ptot,33300000);

	pthread_t threads[NUMTHREADS];
	t_data t_data_array[NUMTHREADS]; // -> memória distribuída	

	for ( i = 0; i< NUMTHREADS; i++ ) {
		init_tdata(&t_data_array[i], i);
		rc = pthread_create(&threads[i], NULL, monte_carlo, &t_data_array[i]);
		if (rc)
			printf("ERRO AO CRIAR A THREAD %d",i);
	}

	//espera o fim de todas as threads
	for ( i = 0; i < NUMTHREADS; i++ )
		pthread_join(threads[i], NULL);

	//adiciona os resultados de todas as threads
	for ( i = 0; i < NUMTHREADS; i++ ) {

		mpf_add(ptot, ptot, t_data_array[i].ptotal);
		mpf_add(pin, pin, t_data_array[i].pdentro);

	}
		
	//calculo do pi
	mpf_div(pi, pin, ptot);
	mpf_mul_ui(pi, pi, 4);
	f=fopen("saida.txt","w");
	gmp_fprintf (f, "%.10000000Ff\n",pi);

	return 0;
}

